<?php
declare(strict_types=1);
namespace Cache\Driver;
use Cache\CacheInterface;

class File implements CacheInterface
{
    /**
     * 缓存前缀
     * @var string|null
     */
    private ?string $prefix;

    /**
     * 初始化
     * @param string|null $prefix
     */
    public function __construct(?string $prefix = null)
    {
        $this->prefix = $prefix;
    }

    /**
     * 取得变量的存储文件名
     *
     * @access private
     * @param string $name 缓存变量名
     * @return string
     */
    private function fileName(string $name): string
    {
        $name = md5 ( $name );
        if (! is_dir ( RUNTIME_PATH.'/cache/' )) {
            mkdir ( RUNTIME_PATH.'/cache/', 0664, true );
        }
        $filename = $this->prefix . $name . '.php';
        return RUNTIME_PATH.'/cache/'.$filename;
    }

    /**
     * 自加
     * @param string $name
     * @param int $step
     * @return bool|int
     */
    public function inc(string $name, int $step = 1): bool|int
    {
        $value = $this->get($name) + $step;
        return $this->set($name,$value);
    }

    /**
     * 自减
     * @param string $name
     * @param int $step
     * @return bool|int
     */
    public function dec(string $name, int $step = 1): bool|int
    {
        $value = $this->get($name) - $step;
        return $this->set($name,$value);
    }

    /**
     * @inheritDoc
     */
    public function get(array|string $name): mixed
    {
        // TODO: Implement get() method.
        $filename = $this->filename ( $name );
        if (! is_file ( $filename )) {
            return false;
        }
        $content = file_get_contents ( $filename );
        if (false !== $content) {
            $expire = ( int ) substr ( $content, 8, 12 );
            if ($expire != 0 && time () > filemtime ( $filename ) + $expire) {
                // 缓存过期删除缓存文件
                unlink ( $filename );
                return false;
            }
            $check = substr ( $content, 20, 32 );
            $content = substr ( $content, 52, - 3 );
            if ($check != md5 ( $content )) { // 校验错误
                return false;
            }
            if (function_exists ( 'gzcompress' )) {
                // 启用数据压缩
                $content = gzuncompress ( $content );
            }
            return unserialize ( $content );
        } else {
            return false;
        }
    }

    /**
     * @inheritDoc
     */
    public function set(string $name, mixed $value, ?int $expire = null): bool
    {
        // TODO: Implement set() method.
        if (is_null ( $expire )) {
            $expire = $this->prefix;
        }
        $filename = $this->filename ( $name );
        $data = serialize ( $value );
        if ( function_exists ( 'gzcompress' )) {
            // 数据压缩
            $data = gzcompress ( $data, 3 );
        }
        $check = md5 ( $data );
        $data = "<?php\n//" . sprintf ( '%012d', $expire ) . $check . $data . "\n?>";
        $result = file_put_contents ( $filename, $data );
        if ($result) {
            clearstatcache ();
            return true;
        } else {
            return false;
        }
    }

    /**
     * @inheritDoc
     */
    public function delete(string $name): bool
    {
        // TODO: Implement delete() method.
        return @unlink ( $this->filename ( $name ) );
    }

    /**
     * @inheritDoc
     */
    public function flush(): bool
    {
        // TODO: Implement flush() method.
        $path = RUNTIME_PATH."/cache/";
        $files = scandir ( $path );
        if ($files) {
            foreach ( $files as $file ) {
                if ($file != '.' && $file != '..' && is_dir ( $path . $file )) {
                    array_map ( 'unlink', glob ( $path . $file . '/*.*' ) );
                } elseif (is_file ( $path . $file )) {
                    unlink ( $path . $file );
                }
            }
            return true;
        }
        return false;
    }
}
